using System;
using System.Diagnostics;
using System.IO;
using System.Xml.Serialization;
using Microsoft.SharePoint;

namespace DE.Sdc.SharePoint.Common.Deployment
{
    [Serializable]
    [XmlType(AnonymousType = true, Namespace = "http://sdc/2009/07/Deployment/1.0/ContentStructure")]
    [XmlRoot(Namespace = "http://sdc/2009/07/Deployment/1.0/ContentStructure", IsNullable = false)]
    public class Folder : BaseItem<Folder>
    {
        public Folder()
        {
            Folders = new HierarchicalList<Folder, Folder>(this);
            Items = new HierarchicalList<Folder, Item>(this);
        }

        /// <remarks/>
        [XmlAttribute]
        public bool Approve { get; set; }

        /// <remarks/>
        [XmlElement]
        public BulkImport BulkImport { get; set; }

        /// <remarks/>
        [XmlArrayItem("Folder", typeof(Folder))]
        public HierarchicalList<Folder, Folder> Folders { get; set; }

        /// <remarks/>
        [XmlArrayItem("Item", typeof(Item))]
        public HierarchicalList<Folder, Item> Items { get; set; }

        /// <summary>
        /// Creates a folder in a given parent folder
        /// </summary>
        /// <param name="folderDefinition">Xml object for a folder</param>
        /// <param name="parentFolder">The parent folder where to create the folder</param>
        /// <returns></returns>
        private static SPFolder CreateFolder(Folder folderDefinition, SPFolder parentFolder)
        {
            Trace.WriteLine("Try to create folder '" + folderDefinition.Title + "' with url '" + folderDefinition.AbsoluteUrl + "'.", "ContentStructureImporter:CreateFolder");

            string folderName = NamedElement.ResolveResource(folderDefinition.Title, parentFolder.ParentWeb.Locale);
            string folderUrl = NamedElement.ResolveResource(folderDefinition.Url, parentFolder.ParentWeb.Locale);

            if (string.IsNullOrEmpty(folderUrl))
                folderUrl = parentFolder.ServerRelativeUrl.TrimEnd('/');

            SPListItem folderItem = parentFolder.ParentWeb.Lists[parentFolder.ParentListId].Folders.Add(folderUrl, SPFileSystemObjectType.Folder, folderName);
            folderItem.Update();

            Trace.WriteLine("Folder '" + folderDefinition.Title + "' with url '" + folderDefinition.AbsoluteUrl + "' created.", "ContentStructureImporter:CreateFolder");

            return folderItem.Folder;
        }

        /// <summary>
        /// Adds, Replaces, Deletes, Modifies or Moves a folder in a given folder
        /// </summary>
        /// <param name="folderDefinition">Xml object for a folder</param>
        /// <param name="folder">A folder based on the folderDefinition or null</param>
        /// <param name="parentFolder">The parent where to import the folder</param>
        /// <param name="primaryKeyFields">The names of the fields that are used for unique identification of a folder or an item</param>
        protected internal void ImportFolder(Folder folderDefinition, SPFolder folder, SPFolder parentFolder, string[] primaryKeyFields)
        {
            Trace.WriteLine("Try to import folder '" + folderDefinition.Title + "' with url '" + folderDefinition.AbsoluteUrl + "'.", "ContentStructureImporter:ImportFolder");

            if (parentFolder != null)
            {
                if (folder == null)
                {
                    if (folderDefinition.Action == ActionType.Add || folderDefinition.Action == ActionType.Replace)
                        folder = CreateFolder(folderDefinition, parentFolder);
                    else
                        return;
                }
                else if (folderDefinition.Action == ActionType.Replace)
                {
                    folder.Delete();
                    folder = CreateFolder(folderDefinition, parentFolder);
                }
                else if (folderDefinition.Action == ActionType.Delete)
                {
                    folder.Delete();
                    return;
                }
            }

            if (folderDefinition.BulkImport != null)
                ImportFolder(folderDefinition.BulkImport, folderDefinition, folder);

            for (int index = 0; index < folderDefinition.Folders.Count; index++)
            {
                Folder subFolderDefinition = folderDefinition.Folders[index];
                string subFolderTitle = NamedElement.ResolveResource(subFolderDefinition.Title,
                                                                    parentFolder == null
                                                                        ? folder.ParentWeb.Locale
                                                                        : parentFolder.ParentWeb.Locale);
                string subFolderUrl = GetRootedUrl(NamedElement.ResolveResource(subFolderDefinition.Url,
                                                                                parentFolder == null
                                                                                    ? folder.ParentWeb.Locale
                                                                                    : parentFolder.ParentWeb.Locale),
                                                   folder.ServerRelativeUrl);

                SPFolder subFolder = null;

                if (!string.IsNullOrEmpty(subFolderUrl))
                    subFolder = folder.ParentWeb.GetFolder(subFolderUrl);
                else
                {
                    try
                    {
                        subFolder = folder.SubFolders[subFolderTitle];
                        if (!subFolder.Exists)
                            subFolder = null;
                    }
                    catch (FileNotFoundException) { }
                    catch (ArgumentNullException) { }
                    catch (ArgumentException) { }
                }

                ImportFolder(subFolderDefinition, subFolder, folder, primaryKeyFields);
            }

            for (int index = 0; index < folderDefinition.Items.Count; index++)
            {
                Item itemDefinition = folderDefinition.Items[index];

                string itemTitle = NamedElement.ResolveResource(itemDefinition.Title,
                                                               parentFolder == null
                                                                   ? folder.ParentWeb.Locale
                                                                   : parentFolder.ParentWeb.Locale);
                string itemUrl = GetRootedUrl(NamedElement.ResolveResource(itemDefinition.Url,
                                                                           parentFolder == null
                                                                               ? folder.ParentWeb.Locale
                                                                               : parentFolder.ParentWeb.Locale),
                                              folder.ServerRelativeUrl);

                SPListItem item = null;

                try
                {
                    if (primaryKeyFields == null)
                        item = folder.ParentWeb.GetListItem(itemUrl ?? folder.ServerRelativeUrl + "/" + itemTitle);
                    else
                    {
                        SPQuery query = new SPQuery();

                        String primaryKeyFieldsQuery = "";
                        for (int i = 0; i < primaryKeyFields.Length; i++)
                        {
                            string primaryKey = primaryKeyFields[i].ToString();
                            if (primaryKey.ToLower() == "title")
                                primaryKeyFieldsQuery += "<Eq><FieldRef Name='Title' /><Value Type='Text'>" + itemDefinition.Title + "</Value></Eq>";
                            else
                            {
                                String primaryKeyValue = "";
                                for (int j = 0; j < itemDefinition.Properties.Count; j++)
                                {
                                    if (itemDefinition.Properties[j].Equals(primaryKey))
                                        primaryKeyValue = itemDefinition.Properties[i].Value;

                                }
                                primaryKeyFieldsQuery += "<Eq><FieldRef Name='" + primaryKey + "' /><Value Type='Text'>" + primaryKeyValue +"</Value></Eq>";
                            }
                        }
                        
                        if (primaryKeyFields.Length > 1)
                            primaryKeyFieldsQuery += "<And>" + primaryKeyFieldsQuery + "</And>";
                        query.Query = "<Where>" + primaryKeyFieldsQuery + "</Where>";
                        item = folder.ParentWeb.GetList(folder.ServerRelativeUrl).GetItems(query)[0];
                    }
                }
                catch (FileNotFoundException) { }
                catch (ArgumentNullException) { }
                catch (ArgumentException) { }

                new Item().ImportItem(itemDefinition, item, folder);
            }

            if (folderDefinition.Action != ActionType.None &&
                folderDefinition.Action <= ActionType.Modify)
                Item.ModifyItemProperties(folderDefinition, folder.Item);

            if (folderDefinition.Approve)
                new Item().ApproveItem(folder.Item);

            Permissions.SetPermissions(folderDefinition, folder.Item, folder.ParentWeb);
            folder.Update();

            Trace.WriteLine("Folder '" + folderDefinition.Title + "' with url '" + folderDefinition.AbsoluteUrl + "' imported.", "ContentStructureImporter:ImportFolder");
        }

        /// <summary>
        /// Imports a folder into a given folder including subfolders, items and files
        /// </summary>
        /// <param name="importDefinition">Xml object for a Bulkimport</param>
        /// <param name="parentFolderDefinition">Xml object for the parent folder</param>
        /// <param name="parentFolder">The parent folder where to insert the bulk import</param>
        private void ImportFolder(BulkImport importDefinition, Folder parentFolderDefinition, SPFolder parentFolder)
        {
            Trace.WriteLine("Try to import bulk into folder '" + parentFolderDefinition.Title + "' with url '" + parentFolderDefinition.AbsoluteUrl + "'.", "ContentStructureImporter:ImportFolder");

            string importPath = importDefinition.Path;
            ActionType importAction = importDefinition.Action;

            if (string.IsNullOrEmpty(importPath))
                throw new ArgumentNullException("Attribute 'Path' for Element 'Import' in Folder '" + parentFolder.Name +
                                                "' is NotSupportedException specified.");

            if (!Directory.Exists(importPath) && importDefinition.IgnoreIfPathNotExists)
                return;

            string[] subFolderPathes = Directory.GetDirectories(importPath);
            string[] filePathes = Directory.GetFiles(importPath);

            for (int index = 0; index < subFolderPathes.Length; index++)
            {
                string subFolderPath = subFolderPathes[index];
                Folder subFolderDefinition = new Folder
                {
                    Action = importAction,
                    Title = System.IO.Path.GetFileName(subFolderPath),
                    BulkImport = new BulkImport { Path = subFolderPath, Action = importAction },
                    Path = subFolderPath,
                    Parent = parentFolderDefinition
                };
                SPFolder subFolder = null;
                try
                {
                    subFolder = parentFolder.SubFolders[subFolderDefinition.Title];
                }
                catch (ArgumentNullException)
                {
                }
                catch (ArgumentException)
                {
                }

                ImportFolder(subFolderDefinition, subFolder, parentFolder, null);
            }

            for (int index = 0; index < filePathes.Length; index++)
            {
                string filePath = filePathes[index];
                Item itemDefinition = new Item
                {  
                    Action = importAction,
                    Title = System.IO.Path.GetFileName(filePath),
                    Path = filePath,
                    Parent = parentFolderDefinition,
                    File = new File { FileAction = FileActionType.Approve }
                };

                SPFile file = null;
                try
                {
                    file = parentFolder.Files[itemDefinition.Title];
                }
                catch (FileNotFoundException) { }
                catch (ArgumentNullException) { }
                catch (ArgumentException) { }

                new Item().ImportItem(itemDefinition, file != null ? file.Item : null, parentFolder);
            }

            Trace.WriteLine("Bulk import into folder '" + parentFolderDefinition.Title + "' with url '" + parentFolderDefinition.AbsoluteUrl + "' imported.", "ContentStructureImporter:ImportFolder");
        }
    }
}